// 得到当前样式值方法
function __getStyleValue(elem: Element, prop: string): string {
  return window.getComputedStyle(elem, null).getPropertyValue(prop);
}
// 计算当前文档滚动值对象方法
function __getDocScrollValue(): ScrollObj {
  return {// 保证兼容性
    scrollX: document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft,// 有生之年可能做pageX
    scrollY: document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
  };
}
// 设置透明度方法
function __setOpacity(elem: HTMLElement, opacity: number | string) {
  if (typeof opacity === "string") {
    opacity = parseFloat(opacity) / 100;
    elem.style.opacity = opacity.toString();
  } else {
    elem.style.opacity = opacity.toString();
  }
}
// 元素设置淡入效果
function __setFadeIn(elem: HTMLElement, speed: number) {
  if (speed > 100) {
    // 安全校验
    console.warn("输入的速率数值大于100，请检查输入的速率值是否合理。");
  }
  __setOpacity(elem, 0); // 保证元素默认的透明度
  let timer: number = 0; // 计时器
  let opacity: number = 0;
  function step() {
    if (opacity >= 100) {
      window.cancelAnimationFrame(timer);
    } else {
      opacity += speed;
      __setOpacity(elem, opacity / 100);
      window.requestAnimationFrame(step);
    }
  }
  window.requestAnimationFrame(step); // 调用动画函数
}
// 元素设置淡出效果
function __setFadeOut(elem: HTMLElement, speed: number) {
  if (speed > 100) {
    // 安全校验
    console.warn("输入的速率数值大于100，请检查输入的速率值是否合理。");
  }
  __setOpacity(elem, "100");
  let opacity: number = 100;
  let timer: number = 0;
  function step() {
    if (opacity <= 0) {
      window.cancelAnimationFrame(timer);
      // 动画结束后向事件管理器发出结束信号，清空回调
      em.trigger('fadeOutDone');
    } else {
      opacity -= speed;
      __setOpacity(elem, opacity / 100);
      window.requestAnimationFrame(step);
    }
  }
  window.requestAnimationFrame(step);
}
// 为元素设置样式方法
function _addClass(elem: HTMLElement, class_name: string) {
  if (elem.classList == undefined) {
    // 退化方法
    const classList = elem.className.split(" ");
    if (classList.indexOf(class_name) === -1) {
      classList.push(class_name);
    }
    elem.className = classList.join(" ");
  } else {
    elem.classList.add(class_name);
  }
}
// 为元素移除样式方法
function _removeClass(elem: HTMLElement, class_name: string) {
  console.log('emit');// 测试
  if (elem.classList == undefined) {
    // 退化方法
    const classList = elem.className.split(" ");
    const removeIndex = classList.indexOf(class_name);
    if (removeIndex > -1) {
      classList.splice(removeIndex, 1);
    }
    elem.className = classList.join(" ");
  } else {
    elem.classList.remove(class_name);
  }
}
// 混入方法
function _mixin(target: IndexObject, ...objs: IndexObject[]) {
  for (let i = 0; i < objs.length; i++) {
    Object.keys(objs[i]).forEach((key) => {
      target[key] = objs[i][key];
    });
  }
  return target;
}
// 节流方法
function _throttle(callback: Function, delay: number) {
  let timer: number = 0;
  const throttled = function (this: any, ...args: any[]) {
    let _this = this;
    clearTimeout(timer);
    timer = setTimeout(() => {
      callback.apply(_this, args);
    }, delay);
  };
  return throttled;
}
// 事件托管对象
const em: EventManager = {
  fadeOutDone: [],
  trigger: function (eventName: string) {
    if (eventName in this) {
      while (this[eventName].length > 0) {
        let callback: Function = this[eventName].shift();
        callback();
      }
    }
  }
};
// 回到顶部组件封装
class ScrollToTop implements ScrollToTopInstance {
  element!: HTMLElement;
  options!: Options;
  isHide!: boolean;
  static version = "0.0.4";
  static defaultStyle = 'scroll-to-top-transition';
  static readonly defaultOptions: Options = {
    showDis: 100,
    fadeSpeed: 20,
    step: 200,
    delayTime: 200,
    cursor: 'pointer'
  };
  constructor(selector: string, options?: any) {
    const elem = document.querySelector(selector);
    if (elem == null) {
      console.error("没有捕捉到的DOM元素，请检查输入的字符串");
      return;
    }
    this.element = elem as HTMLElement;
    this.options = _mixin(
      Object.create(null),
      ScrollToTop.defaultOptions,
      options
    ) as Options;
    this.isHide = false;
    this.init();
  }
  // 初始化方法
  protected init() {
    this.hideElement();
    this.handleClick();
    this.handleScroll();
    this.setImg();
  }
  // 设置图片内容方法
  protected setImg() {
    if (this.options.imgSrc) {
      this.element.style.backgroundImage = `url(${this.options.imgSrc})`;
      this.element.style.backgroundSize = 'cover';// 默认行为
    }
  }
  // 设置指针形状
  protected setCursor() {
    this.element.style.cursor = this.element.style.cursor ? "" : this.options.cursor;
  }
  // 隐藏当前元素方法
  protected hideElement() {
    this.isHide = true;
    __setOpacity(this.element, 0);
  }
  // 返回顶部方法
  protected backToTop(this: ScrollToTop) {
    let timer: number = 0;
    _addClass(this.element, ScrollToTop.defaultStyle);// 添加过渡类样式
    // 将解除样式函数放置于事件管理器中，待动画完毕后触发
    em.fadeOutDone.push(_removeClass.bind(null,this.element, ScrollToTop.defaultStyle));
    const step = () => {
      let yDis = __getDocScrollValue().scrollY;
      if (yDis > 0) {
        document.body.scrollTop = document.documentElement.scrollTop =
          yDis - this.options.step;
        timer = requestAnimationFrame(step);
      } else {
        cancelAnimationFrame(timer);
        if (this.options.callback) {// 触发回调函数
          this.options.callback();
        };
      }
    };
    timer = requestAnimationFrame(step);
  }
  // 处理滚动事件监听的淡入淡出效果
  protected handleScroll(this: ScrollToTop) {
    const toggleStatus = _throttle(() => {
      // 控件出现
      if (__getDocScrollValue().scrollY > this.options.showDis) {
        if (this.isHide) {
          __setFadeIn(this.element, this.options.fadeSpeed);
          this.setCursor();// 出现时设置指针样式
          this.isHide = false;
        }
      } else {// 控件消失
        if (!this.isHide) {
          __setFadeOut(this.element, this.options.fadeSpeed);
          this.setCursor();// 消失时移除指针样式
          this.isHide = true;
        }
      }
    }, this.options.delayTime);
    window.addEventListener("scroll", toggleStatus);
  }
  // 处理点击事件监听
  protected handleClick(this: ScrollToTop) {
    this.element.addEventListener("click", this.backToTop.bind(this));// 绑定this指针指向实例
  }
}
